package core;

import java.util.*;
import persistence.TransactionManager;

import persistence.dto.ItemDTO;

public class Item extends ModelObject implements Comparable{
	/** 
	 * Used for serialization
	 */
	private static final long serialVersionUID = -3367251623738693225L;
	/** product is the type of the item, e.g. Cheetos, Pepsi 6-pack, etc.*/
	private Product product;
	/** the total count of this item*/
	private Calendar expirationDate;
	/** exitTime describes when the Item was removed from the HIT system*/
	private Calendar exitTime;
	/** entryDate specifies when the Item was entered into the HIT system */
	private Calendar entryDate;
	/** itemBarcode is a unique object to identify the Item*/
	private Barcode itemBarcode;
	/** parentContainer is the ProductContainer object where the Item is found*/
	private ProductContainer parentContainer;
	
    private boolean updated;   
    private boolean removed;
    
    /**
     * creates a new itemDTO
     * @return a new itemDTO 
     */
    public ItemDTO createDTO()
    {
        boolean removed = false;
        int pcid = 0;
        if(parentContainer != null)
        {
            pcid = parentContainer.getID();
        }
        if(exitTime != null)
        {
            removed = true;
        }
            
    	return new ItemDTO(getID(),this.getBarcodeString(),
    			entryDate,exitTime,expirationDate, product.getID(), 
                pcid, removed);
    }
    
    
    public boolean isUpdated()
    {
    	return updated;
    }
    
    public void setUpdated(boolean val)
    {
    	updated = val;
    }
        
    //private String prevBarcode;
	/** Constructor Item Class 
	 * Returns a new Item Object according to the given parameters
	 * 
	 * @param product	the product which is the type of the item being created
	 * @param count	the number of occurrences of the new item
	 * @param expirationDate	the date when the item will expire
	 * @param parentContainer	the ProductContainer in which the Item resides
	 * @see				Barcode
	 * @see 			ProductContainer
	 * @see				Product
	 * @throws 			IllegalArgumentException*/
    public Item (Product product, ProductContainer parentContainer, 
            Calendar ed, ItemBarcode iBCode ) throws IllegalArgumentException
    {
            assert(product != null);
            assert(parentContainer != null);
            assert(ed != null);
        //product must be non-empty(Constraint)
        itemBarcode = iBCode;
        this.parentContainer = parentContainer;
        setProduct(product);
        setEntryDate(ed);// current date and time
        setExpirationDate();
        exitTime = null;
        updated = false;
        removed = false;
    }
    
    public Item (Product product, 
            Calendar ed, ItemBarcode iBCode )
    {
    	 //product must be non-empty(Constraint)
        itemBarcode = iBCode;
        this.parentContainer = null;
        setProduct(product);
        setEntryDate(ed);// current date and time
        setExpirationDate();
        exitTime = null;
        updated = false;
        removed = true;
    }
    
    /** uses entry date and product shelf life to calculate expirationDate.
     * this method should be called after the product is set
     * 
     * 
     * @return true if it set
     */
    public boolean setExpirationDate()
    {
        assert product != null;
        
        //Persistence
        if(this.getID() != 0)
        {
	        if(!setExpirationDatePersistence(this))
	        {
	            System.out.println("can't update expiration date in database");
	            return false; //if false, don't run the rest of the method
	        }
        }
        
        
        int months = product.getShelfLife();
        assert(months >= 0);
        if(months == 0)
        {
            expirationDate = null;
            return true;
        }
        expirationDate = new GregorianCalendar(entryDate.get(Calendar.YEAR),
        		                               entryDate.get(Calendar.MONTH),
        		                               entryDate.get(Calendar.DAY_OF_MONTH));
        expirationDate.add(Calendar.MONTH, months);
        return true;
    }
    private static boolean setExpirationDatePersistence(Item item)
    {
        //Persistence
        TransactionManager.begin();
        ItemDTO iDTO = item.createDTO();
        
        boolean success = CoreFacade.getInstance().getFactory().getItemsDAO().edit(iDTO);
        TransactionManager.end(success);
        if(!success){return false;} 
        
        return true;
    }
    
    
    public boolean editExpirationDate()
    {
    	assert product != null;
        
        int months = product.getShelfLife();
        assert(months >= 0);
        if(months == 0)
        {
            expirationDate = null; 
            return true;
        }
        expirationDate = new GregorianCalendar(entryDate.get(Calendar.YEAR),
        		                               entryDate.get(Calendar.MONTH),
        		                               entryDate.get(Calendar.DAY_OF_MONTH));
        expirationDate.add(Calendar.MONTH, months);
        return true;
    }
/**
     * 
     * @param p the product that this items now belongs to
     * @return true if no errors
     */
    public boolean setProduct(Product p)throws IllegalArgumentException
    {
        assert p != null;
        
        if(p == null)
        {
            throw new IllegalArgumentException("product was set to null.");
        }
        
        
        this.product = p;
        return true;
    }
    
    
	/** Returns the ProductContainer in which the item resides
	 * 
	 * @return theProductContainer in which the item resides
	 */
	public ProductContainer getProductContainer()
	{
		return parentContainer;
	}
	
	/** Sets the new ProductContainer in which the item resides
	 *  used for when an Item is moved from one ProductContainer to another
	 *  
	 *  
	 * @param newProdContainer specifies the new ProductContainer where the 
         * Item is found
	 * @return True if no errors were encountered, False if newProdContainer 
         * param was null
	 * @throws IllegalArgumentException newProductContainer must be non null
	 */
	public boolean setProductContainer(ProductContainer newProdContainer) 
			throws IllegalArgumentException
	{
            assert newProdContainer != null;
            
            if(newProdContainer == null)
            {
                throw new IllegalArgumentException
                        ("new Product Container was set to null");
            }
            
            this.parentContainer = newProdContainer;
            return true;
	}
	
	/** Gets the Product which the Item is a part of
	 * 
	 * @return Returns the Product object which contains this Item
	 */
	public Product getProduct()
	{
		return product;
	}
	
	/** Returns the Calendar object associated with the expirationDate
	 * 
	 * @return expirationDate of the Item object
	 */
	public Calendar getExpirationDate()
	{
		return this.expirationDate;
	}
	
	/** Private method used to set the exitTime when the item is removed
	 * 
	 * @param time	specifies when the object was removed from the HIT system
	 * @throws 		IllegalArgumentException
	 */
	public boolean setExitTime(Calendar time) throws IllegalArgumentException
	{
        assert time != null;
        assert (!time.before(entryDate));
        
        if(time == null)
        {
            throw new IllegalArgumentException("time passed in was set to null");
        }
        else if(time.before(entryDate))
        {
            throw new IllegalArgumentException("the exit time is set to be "
                    + "before it's entry time.");
        }
        removed = true;
		this.exitTime = time;
                return true;
	}
	
	/** Private method used to set the Item's ProductContainer to null
	 * 
	 */
	private void clearParentProductContainer()
	{
		this.parentContainer = null;
	}
	
	/** Called when the item is to be removed from the HIT system
	 * 	setExitTime and clearParentProductContainer will be called
	 * @return True if success, False if failure
	 */
	public boolean removeItem()
	{
		this.setExitTime(new GregorianCalendar());
		this.clearParentProductContainer();
		return true;
	}
	
	public void undoRemoveItem(ProductContainer pc)
	{
		exitTime = null;
		setProductContainer(pc);
	}
	
	/** Returns the exitTime of the Item,
	 * 	may be null if item is still in the HIT system.
	 * @return exitTime class variable, may be null
	 */
	public Calendar getExitTime()//throws Exception
	{
            assert (exitTime != null);
            
            return this.exitTime;
	}
        
        public boolean isRemoved()
        {
            if(exitTime != null)
            {
                return true;
            }
            return false;
        }
	
	/** Returns the string barcode associated with the item
	 * 
	 * @return returns the itemBarcode's associated string barcode
	 */
	public Barcode getBarcode()
	{
		return this.itemBarcode;
	}
	
	public String getBarcodeString()
	{
		return this.itemBarcode.getBarcode();
	}
	
	/** Returns the entryDate of the item
	 * 
	 * @return the entryDate of the Item
	 */
	public Calendar getEntryDate()
	{
		return this.entryDate;
	}
	
	/** Allows the user to customly set the entryDate
	 * 
	 * @param newEntryDate the new Calendar date which specifies the entry date
	 * @return True if success, False if failure
	 * @throws IllegalArgumentException
	 */
	public boolean setEntryDate
                (Calendar newEntryDate) throws IllegalArgumentException
	{
        assert newEntryDate != null;
        
        if(newEntryDate == null)
        {
            throw new IllegalArgumentException("new entry date was set to null.");
        }
        Calendar now = new GregorianCalendar();
        if(newEntryDate.after(now))
        {
            throw new IllegalArgumentException("new entry date was set to the future.");
        }
        Calendar earliest = new GregorianCalendar(2000,1,1,0,0);
        if(newEntryDate.before(earliest))
        {
            throw new IllegalArgumentException("new entry date was set too far in past.");
        }
        
            
		this.entryDate = newEntryDate;
		return true;
	}
        
    
        
    public boolean equals(Object obj)
    {
        if(obj == null) return false;
        if(obj == this) return true;
        if(obj.getClass() != this.getClass()) return false;
        
        Item item = (Item) obj;
        
        return item.getBarcode().equals(this.getBarcode());
    }

	@Override
	public int compareTo(Object o)
	{
		return this.entryDate.compareTo(((Item)o).getEntryDate());
	}
    
	
	public void setRemovedTime(Calendar c)
	{
		exitTime = c;
	}
   
	

}
